﻿using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;

namespace WizardConverter
{
    public delegate object Getter();
    public delegate T Getter<T>();
    public delegate void Setter(object o);
    public delegate void Setter<T>(T o);

    public class FieldOrProperty
    {
        public static Process FindSameProcess()
        {
            Process myProcess = Process.GetCurrentProcess();

            foreach (Process p in Process.GetProcessesByName(myProcess.ProcessName))
                if (p.Id != myProcess.Id && p.MainModule.FileName == myProcess.MainModule.FileName)
                    return p;

            return null;
        }

        [DllImport("user32.dll")]
        static extern bool SetForegroundWindow(IntPtr winHandle);

        static void BringToFront()
        {
            Process p = FindSameProcess();

            if (p != null)
                SetForegroundWindow(p.MainWindowHandle);
        }


        private readonly MemberInfo _memberInfo;
        private readonly object _targetObject = null;

        public FieldOrProperty(Type type, string name, bool includePrivate, object targetObject)
            : this(type, name, includePrivate)
        {
            _targetObject = targetObject;
        }

        public FieldOrProperty(Type type, string name, bool includePrivate)
        {
            MemberInfo[] members;

            if (includePrivate)
                members = type.GetMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
            else
                members = type.GetMember(name);

            if (members.Length > 0 && !(members[0] is FieldInfo) && !(members[0] is PropertyInfo))
            {
                throw new AmbiguousMatchException();
            }

            MemberInfo member = null;

            if (members.Length > 1) // Can happen with types created by Reflection.Emit
            {
                for (int i = 0; i < members.Length; i++)
                    if (members[i].DeclaringType == type)
                    {
                        member = members[i];
                        break;
                    }

                if (member == null)
                    member = members[0];
            }
            else
            {
                member = members[0];
            }

            _memberInfo = member;
        }

        public FieldOrProperty(MemberInfo memberInfo)
        {
            _memberInfo = memberInfo;
        }

        public FieldOrProperty(MemberInfo memberInfo, object targetObject)
            : this(memberInfo)
        {
            _targetObject = targetObject;
        }

        public FieldOrProperty OfInstance(object targetObject)
        {
            return new FieldOrProperty(_memberInfo, targetObject);
        }

        public object Value
        {
            get { return GetValue(_targetObject); }
            set { SetValue(_targetObject, value); }
        }

        public string Name
        {
            get { return _memberInfo.Name; }
        }

        public Type Type
        {
            get
            {
                return (_memberInfo is FieldInfo) ? ((FieldInfo)_memberInfo).FieldType :
                        (_memberInfo is PropertyInfo) ? ((PropertyInfo)_memberInfo).PropertyType :
                        null;
            }
        }

        public bool IsField { get { return _memberInfo is FieldInfo; } }
        public bool IsProperty { get { return _memberInfo is PropertyInfo; } }

        private FieldInfo AsField { get { return _memberInfo as FieldInfo; } }
        private PropertyInfo AsProperty { get { return _memberInfo as PropertyInfo; } }

        public Setter SetterDelegate()
        {
            return delegate(object value) { Value = value; };
        }

        public Setter<T> SetterDelegate<T>()
        {
            return delegate(T value) { Value = value; };
        }

        public Getter GetterDelegate()
        {
            return delegate { return Value; };
        }

        public Getter<T> GetterDelegate<T>()
        {
            return delegate { return (T)Value; };
        }

        public object GetValue(object targetObject)
        {
            if (IsField)
                return AsField.GetValue(targetObject);

            return AsProperty.GetValue(targetObject, null);
        }

        public void SetValue(object targetObject, object value)
        {
            if (IsField)
                AsField.SetValue(targetObject, value);
            else
                AsProperty.SetValue(targetObject, value, null);
        }

        public bool IsStatic
        {
            get
            {
                if (IsField)
                    return AsField.IsStatic;

                return (AsProperty.CanRead && AsProperty.GetGetMethod(true).IsStatic) || (AsProperty.CanWrite && AsProperty.GetSetMethod(true).IsStatic);
            }
        }
    }
}